package mongo

import (
	"errors"
	"fmt"

	"github.com/dop251/goja"
	"go.mongodb.org/mongo-driver/bson"
)

// 判断是否为Object
func isObject(v goja.Value) bool {
	if v == nil {
		return false
	}
	_, ok := v.(*goja.Object)
	return ok
}

// 判断是否是数组
func isArray(v goja.Value, vm *goja.Runtime) bool {
	if !isObject(v) {
		return false
	}

	obj := v.ToObject(vm)
	// 获取数组的构造函数
	arrConstructor := vm.Get("Array")
	// 检查对象的构造函数是否为Array
	return obj.Get("constructor").Equals(arrConstructor)
}

// 辅助函数：将goja.Value转换为bson.M
func valueToBson(v goja.Value, vm *goja.Runtime) (bson.M, error) {
	if goja.IsUndefined(v) || goja.IsNull(v) {
		return bson.M{}, nil
	}

	obj := v.ToObject(vm)
	keys := obj.Keys()

	result := make(bson.M, len(keys))

	for _, key := range keys {
		val := obj.Get(key)

		if val.ExportType() == nil {
			result[key] = nil
			continue
		}

		// 处理嵌套对象
		if isObject(val) && !isArray(val, vm) {
			nested, err := valueToBson(val, vm)
			if err != nil {
				return nil, err
			}
			result[key] = nested
		} else if isArray(val, vm) {
			arrVal := val.ToObject(vm)
			length := arrVal.Get("length").ToInteger()
			arr := make([]interface{}, 0, length)
			for i := 0; i < int(length); i++ {
				elem := arrVal.Get(fmt.Sprintf("%d", i))
				if isObject(elem) {
					nested, err := valueToBson(elem, vm)
					if err != nil {
						return nil, err
					}
					arr = append(arr, nested)
				} else {
					arr = append(arr, elem.Export())
				}
			}
			result[key] = arr
		} else {
			result[key] = val.Export()
		}
	}

	return result, nil
}

// 辅助函数：将goja数组转换为bson.M数组
func arrayToBsonArray(v goja.Value, vm *goja.Runtime) ([]interface{}, error) {
	if goja.IsUndefined(v) || goja.IsNull(v) {
		return []interface{}{}, nil
	}

	if !isArray(v, vm) {
		return nil, errors.New("输入不是数组")
	}

	obj := v.ToObject(vm)
	length := obj.Get("length").ToInteger()

	result := make([]interface{}, 0, length)

	for i := 0; i < int(length); i++ {
		elem := obj.Get(fmt.Sprintf("%d", i))
		if isObject(elem) {
			bsonElem, err := valueToBson(elem, vm)
			if err != nil {
				return nil, err
			}
			result = append(result, bsonElem)
		} else {
			result = append(result, elem.Export())
		}
	}

	return result, nil
}
